package org.hawk.gwt.ppc.introspection.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.hawk.gwt.ppc.introspection.JavaConstructor;
import org.hawk.gwt.ppc.introspection.JavaField;
import org.hawk.gwt.ppc.introspection.JavaMethod;
import org.hawk.gwt.ppc.introspection.JavaModifier;
import org.hawk.gwt.ppc.introspection.JavaTypeScope;
import org.hawk.gwt.ppc.introspection.JavaTypeVariable;
import org.hawk.gwt.ppc.utils.Name;

/**
 * Type scope for binary types. This types are created when there is no source provided for them.
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class BinaryJavaTypeScope extends AbstractJavaTypeScope {
	
	private Class<?> type;

	BinaryJavaTypeScope(Class<?> type, JavaScopeImpl parent) {
		super(Name.getRawName(type.getName()), getTypeName(type.getGenericSuperclass()), getInterfacesNames(type));
		
		this.type = type;
		init(null, parent);
		setModifiers(JavaModifier.fromMask(type.getModifiers()));
	}

	private static List<String> getInterfacesNames(Class<?> type) {
		List<String> result = new LinkedList<String>();
		for (Type iface : type.getGenericInterfaces()) {
			result.add(getTypeName(iface));
		}
		return result;
	}

	private static String getTypeName(Type type) {
		if (type instanceof Class) {
			return ((Class<?>)type).getName();
		}
		return type == null ? null : type.toString();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isInterface()
	 */
	public boolean isInterface() {
		return type.isInterface();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isClass()
	 */
	public boolean isClass() {
		return !type.isEnum() && !type.isInterface() && !type.isAnnotation();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isEnum()
	 */
	public boolean isEnum() {
		return type.isAnnotation();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isAnnotation()
	 */
	public boolean isAnnotation() {
		return type.isAnnotation();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#getConstructors()
	 */
	public List<JavaConstructor> getConstructors() {
		Constructor<?>[] constructors = type.getDeclaredConstructors();
		List<JavaConstructor> result = new ArrayList<JavaConstructor>(constructors.length);
		for (Constructor<?> c : constructors) {
			result.add(new JavaConstcructorImpl(c, this));
		}
		return Collections.unmodifiableList(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#getMethods()
	 */
	public List<JavaMethod> getMethods() {
		Method[] methods = type.getDeclaredMethods();
		List<JavaMethod> result = new ArrayList<JavaMethod>(methods.length);
		for (Method m : methods) {
			result.add(new JavaMethodImpl(m, this));
		}
		return Collections.unmodifiableList(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#getFields()
	 */
	public List<JavaField> getFields() {
		Field[] fields = type.getDeclaredFields();
		List<JavaField> result = new ArrayList<JavaField>(fields.length);
		for (Field field : fields) {
			result.add(new JavaFieldImpl(field, this));
		}
		return Collections.unmodifiableList(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#getField(java.lang.String)
	 */
	JavaField getDeclaredField(String name) {
		try {
			Field field = type.getDeclaredField(name);
			return new JavaFieldImpl(field, this);
		} catch(NoSuchFieldException ex) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#getDeclaredClasses()
	 */
	public List<JavaTypeScope> getDeclaredClasses() {
		Class<?>[] classes = type.getDeclaredClasses(); 
		List<JavaTypeScope> result = new ArrayList<JavaTypeScope>(classes.length);
		for (Class<?> cls : type.getDeclaredClasses()) {
			result.add(new BinaryJavaTypeScope(cls, this));
		}
		return Collections.unmodifiableList(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isPrimitive()
	 */
	public boolean isPrimitive() {
		return type.isPrimitive();
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeScope#isPrimitiveWrapper()
	 */
	public boolean isPrimitiveWrapper() {
		return PrimitiveTypeScopes.WRAPPERS.containsKey(type.getName());
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaParameterizable#getTypeVariables()
	 */
	public List<JavaTypeVariable> getTypeVariables() {
		return GenericHelper.buildTypeVariables(type.getTypeParameters(), this);
	}
}
